/****************************************************************************
 *   oboe.controllers.StateMachine					 						*
 *   Copyright (C) 2011 by Felipe Manga										*
 *   lufeboma@gmail.com														*
 *									 										*
 *   Permission is hereby granted, free of charge, to any person obtaining 	*
 *   a copy of this software and associated documentation files (the	   	*
 *   "Software"), to deal in the Software without restriction, including   	*
 *   without limitation the rights to use, copy, modify, merge, publish,   	*
 *   distribute, sublicense, and/or sell copies of the Software, and to		*
 *   permit persons to whom the Software is furnished to do so, subject to 	*
 *   the following conditions:						 						*
 *									 										*
 *   The above copyright notice and this permission notice shall be			*
 *   included in all copies or substantial portions of the Software.	   	*
 *									 										*
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,	   	*
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF		*
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR	 	*
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 	*
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 	*
 *   OTHER DEALINGS IN THE SOFTWARE.					   					*
 ***************************************************************************/

package oboe.controllers;

#if flash
import flash.display.Sprite;
import flash.events.Event;
import flash.Lib;
#end
import haxe.Timer;
import oboe.core.ISigSlot;
import oboe.core.Oboe;
import oboe.core.SigSlot;

using oboe.util.ISigSlotHelper;

class StateMachine implements ISigSlot
{
	var sigslot:SigSlot;

	private var states : Hash< ISigSlot >;

	private var activeState : ISigSlot;
	private var stateParams : Array<Dynamic>;
	private var nextState : ISigSlot;

	public static inline var SIG_ONADDTOSTAGE:String = "onAddToStage";
	public static inline var SIG_ONTICK:String = "onTick";
	public static inline var SIG_ONEXITSTATE:String = "onExitState";
	public static inline var SIG_ONENTERSTATE:String = "onEnterState";

	public function new( ?base:ISigSlot=null, ?legacy:Bool=false )
	{
		if ( base == null ) base = new SigSlot();
		this.init( base );
		this.activeState = null;
		this.nextState = null;
		
		this.states = new Hash< ISigSlot >();
	}
	
	@slot public function addState( key:String, state:ISigSlot ):Void
	{
		states.set( key, state );
	}

	@slot public function setState( key:String, ?params:Array<Dynamic> ):Void
	{
		this.nextState = states.get( key );
		this.stateParams = params;
		this.broadcastSignal("onSetState", [this]);
	}
	
	@slot public function resetState( key:String, ?params:Array<Dynamic> ):Void
	{
		var state = states.get( key );
		var c = Type.getClass( state );
		states.set( key, this.nextState = Type.createInstance( c, [] ) );
		this.stateParams = params;
		this.broadcastSignal("onSetState", [this]);
	}
	
	inline function addChild( c:ISigSlot ):Void
	{
		this.sigslot.addChild(c);
	}
	
	function removeChild( c:ISigSlot ):Void
	{
		this.sigslot.removeChild(c);
	}
	
	public function forceStateUpdate():Void
	{
		this.onPreTick(0);
	}
	
	@slot function onPreTick( time:Float ):Void
	{
		this.forceApplyState();
	}

	private inline function forceApplyState():Void
	{
		var ns = this.nextState;
		var as = this.activeState;
		if( ns != as )
		{
			this.addChild( ns );
			
			if ( this.stateParams == null ) this.stateParams = [];
			
			ns.message( SIG_ONENTERSTATE, this.stateParams );
			if( as != null )
			{
				as.message( SIG_ONEXITSTATE, [] );
				this.removeChild( as );
			}
			
			this.activeState = ns;
			this.stateParams = null;
		}
	}
}